package com.genreate.util.pay.ali.enums;

import lombok.Getter;

import java.util.HashMap;
import java.util.Map;

/**
 * 支付宝接口错误码枚举类
 * 包含HTTP状态码、错误码、错误描述及解决方案
 */
@Getter
public enum AlipayErrorCodeEnum {

    // ====================== 200 成功 ======================
    SUCCESS(200, "-", "接口调用成功，调用结果请参考具体的API所对应的业务返回参数。", "-"),

    // ====================== 400 错误 ======================
    // 服务不可用
    UNKNOWN_ERROR(400, "unknow-error", "业务异常，服务暂不可用", "稍后重试"),

    // 缺少必选参数
    MISSING_METHOD(400, "missing-method", "缺少方法名参数", "请求参数里面必须要有 method 参数"),
    MISSING_SIGNATURE(400, "missing-signature", "缺少签名参数", "检查请求参数，缺少 sign 参数"),
    MISSING_SIGNATURE_TYPE(400, "missing-signature-type", "缺少签名类型参数", "检查请求参数，缺少 sign_type 参数"),
    MISSING_SIGNATURE_KEY(400, "missing-signature-key", "缺少签名配置", "未上传公钥配置"),
    MISSING_APP_ID(400, "missing-app-id", "缺少appId参数", "检查请求参数，缺少 appId 参数"),
    MISSING_TIMESTAMP(400, "missing-timestamp", "缺少时间戳参数", "检查请求参数，缺少 timestamp 参数"),
    MISSING_VERSION(400, "missing-version", "缺少版本参数", "检查请求参数，缺少 version 参数"),
    DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE(400, "decryption-error-missing-encrypt-type", "解密出错，未指定加密算法", "检查参数，缺少 encrypt_type 参数"),

    // 非法的参数
    INVALID_PARAMETER(400, "invalid-parameter", "参数无效", "检查参数，格式不对、非法值、越界等"),
    UPLOAD_FAIL(400, "upload-fail", "文件上传失败", "文件写入失败，重试"),
    INVALID_FILE_EXTENSION(400, "invalid-file-extension", "文件扩展名无效", "检查传入的文件扩展名称，目前支持格式：csv、txt、zip、rar、gz、doc、docx、xls、xlsx、pdf、bmp、gif、jpg、jpeg、png"),
    INVALID_FILE_SIZE(400, "invalid-file-size", "文件大小无效", "检查文件大小，目前支持最大为：50MB"),
    INVALID_METHOD(400, "invalid-method", "不存在的方法名", "检查入参 method 是否正确"),
    INVALID_FORMAT(400, "invalid-format", "无效的数据格式", "检查入参 format，目前只支持 JSON 和 XML 2种格式"),
    INVALID_SIGNATURE_TYPE(400, "invalid-signature-type", "无效的签名类型", "检查入参 sign_type，目前只支持 RSA、RSA2、HMAC_SHA1"),
    INVALID_SIGNATURE(400, "invalid-signature", "无效签名", "公私钥是否是一对；检查公钥上传是否与私钥匹配；存在中文需要做 urlencode；签名算法是否无误"),
    INVALID_TOKEN(400, "invalid-token", "无效令牌", "auth_token 为无效的令牌，请确认令牌有效"),
    INVALID_ENCRYPT_TYPE(400, "invalid-encrypt-type", "无效的加密类型", "检查入参 encrypt_type，目前只支持 AES"),
    INVALID_ENCRYPT(400, "invalid-encrypt", "解密异常", "重试"),
    INVALID_APP_ID(400, "invalid-app-id", "无效的appId参数", "检查入参 app_id，app_id 不存在，或者未上线"),
    INVALID_TIMESTAMP(400, "invalid-timestamp", "非法的时间戳参数", "时间戳参数 timestamp 非法，请检查格式需要为\"yyyy-MM-dd HH:mm:ss\""),
    INVALID_CHARSET(400, "invalid-charset", "字符集错误", "请求参数 charset 错误，目前支持格式：GBK、UTF-8"),
    INVALID_DIGEST(400, "invalid-digest", "摘要错误", "检查请求参数，文件摘要参数必填"),
    DECRYPTION_ERROR_NOT_VALID_ENCRYPT_TYPE(400, "decryption-error-not-valid-encrypt-type", "解密出错，不支持的加密算法", "检查入参 encrypt_type，目前只支持 AES"),
    DECRYPTION_ERROR_NOT_VALID_ENCRYPT_KEY(400, "decryption-error-not-valid-encrypt-key", "解密出错，未配置加密密钥或加密密钥格式错误", "没有配置加密密钥"),
    DECRYPTION_ERROR_UNKNOWN(400, "decryption-error-unknown", "解密出错，未知异常", "重试"),
    NOT_SUPPORT_APP_AUTH(400, "not-support-app-auth", "本接口不支持第三方代理调用", "本接口不支持第三方代理调用"),
    SUSPECTED_ATTACK(400, "suspected-attack", "可疑的攻击请求", "可查看自定义参数说明，商家的请求参数中，所有的 key（支付宝关键 key 或者商家自己的 key），其对应的 value 中都不应该出现支付宝关键key，如 body、subject 等，否则可能被拦截，请更换参数名称"),
    FORBIDDEN_API(400, "forbidden-api", "接口被禁用", "接口已被禁用，请查看接口说明"),
    APP_CERT_EXPIRED(400, "app-cert-expired", "应用公钥证书已经不在有效期内", "可查看证书到期更新指南进行处理"),
    INVALID_CONTENT_TYPE(400, "invalid-content-type", "contentType 错误", "HTTP 请求的 ContentType 非法，请检查 HTTP ContentType，可参考文档"),
    NOT_ONLINE_APP(400, "not-online-app", "应用未上线", "应用未上线，请在控制台上线应用后调用"),
    UNSUPPORTED_SDK_VERSION(400, "unsupported-sdk-version", "不支持的 SDK 版本", "SDK 版本不支持，请升级到最新版本 SDK，参考 SDK说明"),
    APP_OPENID_NOT_MATCH(400, "app-openid-not-match", "appid 和 openid 不匹配", "应用 id 和 openid 不匹配，请确认 openid 是否归属该应用"),
    OPENID_UID_NOT_MATCH(400, "openid-uid-not-match", "openid 和 uid 不匹配", "用户 uid 和 openid 不匹配，请确认 openid 和 uid 关联是否正确"),
    INVALID_OPENID(400, "invalid-openid", "openid 参数值非法", "openid 非法，请确认 openid 是从支付宝获取"),
    ILLEGAL_JSON(400, "illegal-json", "不正确的 JSON 格式", "JSON 格式非法，请检查参数内容"),
    INVALID_HTTP_METHOD(400, "invalid-http-method", "http 方法错误", "请检查 http 请求方法，应和接口文档中要求的 http 方法一致，一般为 post 或 get"),

    // 条件异常
    INVALID_AUTH_RELATIONS(400, "invalid-auth-relations", "无效的授权关系", "用户授权校验失败，请确认用户是否已经授权给相关接口和 scope"),
    MISSING_SIGNATURE_CONFIG(400, "missing-signature-config", "验签出错，未配置对应签名算法的公钥或者证书", "可查看验签出错，应用未配置对应签名算法的公钥或者证书进行处理"),

    // 业务处理失败
    BUSINESS_PROCESS_FAILED(400, "-", "对应业务错误码，明细错误码和解决方案请参见具体的 API 说明", "-"),

    // ====================== 401 错误 ======================
    // 授权权限不足
    INVALID_AUTH_TOKEN(401, "invalid-auth-token", "无效的访问令牌", "请刷新授权令牌或重新授权获取新的令牌"),
    AUTH_TOKEN_TIME_OUT(401, "auth-token-time-out", "访问令牌已过期", "请刷新授权令牌或重新授权获取新的令牌"),
    INVALID_APP_AUTH_TOKEN(401, "invalid-app-auth-token", "无效的应用授权令牌", "请刷新应用授权令牌或重新授权获取新的令牌"),
    INVALID_APP_AUTH_TOKEN_NO_API(401, "invalid-app-auth-token-no-api", "商户未授权当前接口", "请重新授权获取新的应用授权令牌"),
    APP_AUTH_TOKEN_TIME_OUT(401, "app-auth-token-time-out", "应用授权令牌已过期", "请刷新应用授权令牌或重新授权获取新的令牌"),
    NO_PRODUCT_REG_BY_PARTNER(401, "no-product-reg-by-partner", "商家未签约任何产品", "服务商代理调用的场景，请上线商家的服务窗"),

    // 权限不足
    INSUFFICIENT_ISV_PERMISSIONS(401, "insufficient-isv-permissions", "ISV 权限不足", "请检查配置的账户是否有当前接口权限。请在开放平台控制台找到对应的应用，在其产品列表中添加缺少的功能包、签约或申请必须的信息。详情可查看产品绑定"),
    INSUFFICIENT_USER_PERMISSIONS(401, "insufficient-user-permissions", "用户权限不足", "代理的商家没有当前接口权限，服务商可以选择让商家的应用自主添加功能后签约；或选择协助商家开通产品"),
    SELF_INVOKE_FORBIDDEN(401, "self-invoke-forbidden", "此用户不允许自调用", "应用类型为第三方应用时，不允许直接调用该接口，必须通过授权方式代理其他应用调用。需要更换应用类型或者使用代调用方式"),

    // ====================== 429 错误 ======================
    // 调用频次超限
    APP_CALL_LIMITED(429, "app-call-limited", "应用调用次数超限，包含调用频率超限", "降低请求并发量"),
    METHOD_CALL_LIMITED(429, "method-call-limited", "API 调用次数超限，包含调用频率超限", "降低请求并发量"),

    // ====================== 500 错误 ======================
    SYSTEM_ERROR(500, "unknow-error", "系统异常，服务暂不可用", "稍后重试"),

    // ACQ前缀错误码
    ACQ_SYSTEM_ERROR(400, "ACQ.SYSTEM_ERROR", "系统错误", "重新发起请求"),
    ACQ_ACCESS_FORBIDDEN(400, "ACQ.ACCESS_FORBIDDEN", "无权限使用接口", "联系支付宝小二签约"),
    ACQ_BUYER_SELLER_EQUAL(400, "ACQ.BUYER_SELLER_EQUAL", "买卖家不能相同", "更换买家重新付款"),
    ACQ_CONTEXT_INCONSISTENT(400, "ACQ.CONTEXT_INCONSISTENT", "交易信息被篡改", "确认该笔交易信息是否为当前买家的已存订单，如果是则认为本次请求参数与已存交易不一致，如果不是则更换商家订单号后，重新发起请求"),
    ACQ_EXIST_FORBIDDEN_WORD(400, "ACQ.EXIST_FORBIDDEN_WORD", "订单信息中包含违禁词", "修改订单信息后，重新发起请求"),
    ACQ_PARTNER_ERROR(400, "ACQ.PARTNER_ERROR", "应用APP_ID填写错误", "联系支付宝小二，确认APP_ID的状态"),
    ACQ_RISK_MERCHANT_IP_NOT_EXIST(400, "ACQ.RISK_MERCHANT_IP_NOT_EXIST", "当前交易未传入IP信息，创单失败，请传入IP后再发起支付", "检查请求参数是否已经传入用户IP信息"),
    ACQ_SELLER_BEEN_BLOCKED(400, "ACQ.SELLER_BEEN_BLOCKED", "商家账号被冻结", "联系支付宝小二，解冻账号"),
    ACQ_TOTAL_FEE_EXCEED(400, "ACQ.TOTAL_FEE_EXCEED", "订单总金额不在允许范围内", "修改订单金额再发起请求"),
    ACQ_TRADE_BUYER_NOT_MATCH(400, "ACQ.TRADE_BUYER_NOT_MATCH", "交易买家不匹配", "该笔交易已经在支付宝端创建，但请求买家与已存交易中的买家不一致。请商户确认本次请求是否与已存交易有关，若为同一笔交易，则只能用原始买家付款，若无关更换商家订单号后，重新发起请求"),
    ACQ_TRADE_HAS_SUCCESS(400, "ACQ.TRADE_HAS_SUCCESS", "交易已被支付", "确认该笔交易信息是否为当前买家的，如果是则认为交易付款成功，如果不是则更换商家订单号后，重新发起请求"),

    // 新增退款相关错误码
    ACQ_ALLOC_AMOUNT_VALIDATE_ERROR(400, "ACQ.ALLOC_AMOUNT_VALIDATE_ERROR", "退分账金额超限", "请调整退分账金额后重试"),
    ACQ_BUYER_ENABLE_STATUS_FORBID_REFUND(400, "ACQ.BUYER_ENABLE_STATUS_FORBID", "买家状态异常", "联系支付宝小二确认买家状态异常原因，或者可联系买家进行线下退款处理"),
    ACQ_BUYER_ERROR(400, "ACQ.BUYER_ERROR", "买家状态异常。", "联系支付宝小二确认买家状态异常原因，或者可联系买家进行线下退款处理"),
    ACQ_BUYER_NOT_EXIST(400, "ACQ.BUYER_NOT_EXIST", "买家不存在", "买家已经注销账号，建议联系买家进行线下退款处理"),
    ACQ_CURRENCY_NOT_SUPPORT(400, "ACQ.CURRENCY_NOT_SUPPORT", "退款币种不支持", "请确认传入的退款币种是否正确"),
    ACQ_CUSTOMER_VALIDATE_ERROR(400, "ACQ.CUSTOMER_VALIDATE_ERROR", "账户已注销或者被冻结", "请查询账户状态：1. 如果账户已注销，请线下处理；2. 如果账户已冻结，请联系支付宝小二确认冻结原因。"),
    ACQ_DISCORDANT_REPEAT_REQUEST(400, "ACQ.DISCORDANT_REPEAT_REQUEST", "请求信息不一致", "退款请求号对应的退款已经执行成功，且本次请求的退款金额与之前请求的金额不一致，请检查传入的退款金额是否正确。 或者通过退款查询接口获取退款执行结果。"),
    ACQ_ENTERPRISE_PAY_BIZ_ERROR(400, "ACQ.ENTERPRISE_PAY_BIZ_ERROR", "因公付业务异常", "如果提示“当前交易不含企业出资”，请确认交易是否包含企业出资，如果不包含则接口入参不能指定enterprise_pay_info参数，如果确认包含则联系支付宝核实。 如果提示“无效企业退款金额”，请检查指定的企业退款金额是否超过当前交易企业支付的金额。 其它情况，请联系支付宝小二。"),
    ACQ_INVALID_PARAMETER_REFUND(400, "ACQ.INVALID_PARAMETER", "参数无效", "请根据接口返回的错误信息，检查请求参数，修改后重新发起请求"),
    ACQ_NOT_ALLOW_PARTIAL_REFUND(400, "ACQ.NOT_ALLOW_PARTIAL_REFUND", "不支持部分退款", "由于交易使用了特定的优惠券等场景，该笔交易不支持部分退款，请对交易进行全额退款或者联系买家进行线下退款处理"),
    ACQ_ONLINE_TRADE_VOUCHER_NOT_ALLOW_REFUND(400, "ACQ.ONLINE_TRADE_VOUCHER_NOT_ALLOW_REFUND", "交易不允许退款", "此交易中核销了购买的代金券，不允许进行退款，可联系买家进行线下退款处理"),
    ACQ_OVERDRAFT_AGREEMENT_NOT_MATCH(400, "ACQ.OVERDRAFT_AGREEMENT_NOT_MATCH", "垫资退款接口传入模式和签约配置不一致", "请检查垫资退款合约中的出资方式，修改合约或接口传参后重试"),
    ACQ_OVERDRAFT_ASSIGN_ACCOUNT_INVALID(400, "ACQ.OVERDRAFT_ASSIGN_ACCOUNT_INVALID", "垫资退款出资账号和商户信息不一致", "垫资退款出资账号必须为商户名下支付宝账号，请更换出资账号后重试"),
    ACQ_REASON_TRADE_BEEN_FREEZEN(400, "ACQ.REASON_TRADE_BEEN_FREEZEN", "请求退款的交易被冻结", "联系支付宝小二，确认该笔交易的具体情况"),
    ACQ_REASON_TRADE_REFUND_FEE_ERR(400, "ACQ.REASON_TRADE_REFUND_FEE_ERR", "退款金额无效", "同一笔交易累计请求的退款金额不能大于交易总金额，请检查退款请求的金额是否正确。"),
    ACQ_REASON_TRADE_STATUS_INVALID(400, "ACQ.REASON_TRADE_STATUS_INVALID", "交易状态异常", "查询交易，确认交易是否是支付成功状态，是的话可联系支付宝小二确认交易状态"),
    ACQ_REFUNDALLOC_UNAUTH_LIMIT(400, "ACQ.REFUNDALLOC_UNAUTH_LIMIT", "分账接收方未开启分账回退，退分账失败", "由于分账接收方未开启分账回退，退分账暂时无法调用，请联系分账接收方在 支付宝商家平台 > 资金管理 > 资金服务 > 分账接收 设置中，开启分账回退授权 后再发起调用"),
    ACQ_REFUND_ACCOUNT_NOT_EXIST(400, "ACQ.REFUND_ACCOUNT_NOT_EXIST", "退款出资账号不存在或账号异常", "检查退款出资账号状态，账号正常后重试"),
    ACQ_REFUND_AMT_NOT_EQUAL_TOTAL(400, "ACQ.REFUND_AMT_NOT_EQUAL_TOTAL", "退款金额超限", "1、请检查退款金额是否正确，请求的退款金额不能大于交易总金额； 2、如果不是全额退款，退款请求号必填，请检查是否传入了退款请求号；"),
    ACQ_REFUND_CHARGE_ERROR(400, "ACQ.REFUND_CHARGE_ERROR", "退收费异常", "请过一段时间后再重试发起退款"),
    ACQ_REFUND_FEE_ERROR(400, "ACQ.REFUND_FEE_ERROR", "交易退款金额有误", "请检查传入的退款金额是否正确"),
    ACQ_REFUND_ROYALTY_PAYEE_ACCOUNT_NOT_EXIST(400, "ACQ.REFUND_ROYALTY_PAYEE_ACCOUNT_NOT_EXIST", "退分账收入方账户不存在", "退分账收入方账户不存在，请确认收入方账号是否正确，更换账号后重新发起"),
    ACQ_SELLER_BALANCE_NOT_ENOUGH(400, "ACQ.SELLER_BALANCE_NOT_ENOUGH", "卖家余额不足", "商户支付宝账户充值后重新发起退款即可"),
    ACQ_TRADE_HAS_CLOSE_REFUND(400, "ACQ.TRADE_HAS_CLOSE", "交易已关闭", "该交易已关闭，不能再进行退款，请确认请求退款的交易是否未支付或者已完成退款"),
    ACQ_TRADE_HAS_FINISHED(400, "ACQ.TRADE_HAS_FINISHED", "交易已完结", "该交易已完结（已超过退款期限），不允许进行退款（即使重试也无法成功），建议联系买家进行线下退款处理。"),
    ACQ_TRADE_NOT_ALLOW_REFUND(400, "ACQ.TRADE_NOT_ALLOW_REFUND", "当前交易不允许退款", "检查当前交易的状态是否为交易成功状态以及签约的退款属性是否允许退款，确认后，重新发起请求"),
    ACQ_TRADE_NOT_EXIST_REFUND(400, "ACQ.TRADE_NOT_EXIST", "交易不存在", "检查请求中的交易号和商户订单号是否正确，确认后重新发起"),
    ACQ_TRADE_SETTLE_ERROR(400, "ACQ.TRADE_SETTLE_ERROR", "交易结算异常", "请检查传入的退结算项信息是否正确，如果正确请联系支付宝小二"),
    ACQ_TRADE_STATUS_ERROR(400, "ACQ.TRADE_STATUS_ERROR", "交易状态非法", "查询交易，确认交易是否已经付款"),
    ACQ_USER_NOT_MATCH_ERR(400, "ACQ.USER_NOT_MATCH_ERR", "交易用户不匹配", "请联系支付宝小二处理"),
    ;

    /**
     * 哈希表缓存：key=错误码，value=枚举实例
     */
    private static final Map<String, AlipayErrorCodeEnum> CODE_MAP = new HashMap<>();

    static {
        for (AlipayErrorCodeEnum errorCodeEnum : values()) {
            CODE_MAP.put(errorCodeEnum.code, errorCodeEnum);
        }
    }


    /**
     * HTTP状态码
     */
    private final int httpStatusCode;

    /**
     * 错误码
     */
    private final String code;

    /**
     * 错误描述
     */
    private final String msg;

    /**
     * 解决方案
     */
    private final String solution;

    AlipayErrorCodeEnum(int httpStatusCode, String code, String msg, String solution) {
        this.httpStatusCode = httpStatusCode;
        this.code = code;
        this.msg = msg;
        this.solution = solution;
    }

    public static void main(String[] args) {
        AlipayErrorCodeEnum[] values = AlipayErrorCodeEnum.values();
        Map<String,Integer> map = new HashMap<>();
        for (AlipayErrorCodeEnum value : values) {
            String code = value.getCode();
            Integer integer = map.get(code);
            if(integer == null){
                integer = 1;
            }else {
                integer++;
            }
            map.put(code,integer);
        }

        map.forEach((k,v)->{if(v>1){System.out.println(k+"="+v);}});
    }

    /**
     * 根据错误码获取枚举实例（忽略HTTP状态码，用于快速匹配）
     *
     * @param code 错误码
     * @return 匹配的枚举实例，无匹配时返回null
     */
    public static AlipayErrorCodeEnum getByCode(String code) {
        return CODE_MAP.get(code);
    }

    /**
     * 根据错误码获取错误消息
     *
     * @param code 代码
     * @return {@link String }
     */
    public static String getMsgByCode(String code) {
        AlipayErrorCodeEnum errorCodeEnum = getByCode(code);
        if (errorCodeEnum != null) {
            return errorCodeEnum.msg;
        }
        return null;
    }
}